home *** CD-ROM | disk | FTP | other *** search
-
- |====================================|
- | |
- | TELEMACHOS proudly presents : |
- | |
- | Part 1 of the PXD trainers - |
- | |
- | DOOM-WALLS |
- | the technique and tricks! |
- | |
- |====================================|
-
- ___---__--> The Peroxide Programming Tips <--__---___
-
- <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
-
-
- Intoduction
- -----------
-
- "Hi there! This is Telemachos of HASH. Now you might be wondering who the hell
- HASH is. Well - HASH is a great upcomming danish group. We make everything
- from PPE's, demos, utilities, GFX, ANSI, an e-mag, music and now also
- programming tuturials. So keep an eye out for future releases from us :)"
-
- Sigh! Well, some of you might have read the lines above before... YES this IS
- a re-release.
- But before nukin' this file from your HD please read the new information below.
-
- The truth is : HASH died... but Peroxide raised from the dust - featuring many
- new talented members. The main reason why HASH died is, that some of the members
- were more serious than others. Myself along with our founder Candyman, our
- musician Punqtured and our Net-coordinator Notion wanted the group to be a
- group know for its great releases - but rather the group became known as a
- bunch of ever-arguing, ever-flaming idiots constantly in war with another
- danish demogroup called The Renegades.
- So we decided to kill the group and together we formed Peroxide with the 4
- before mentioned members as Seniors and a bunch of new (and some old) guys as
- group members.
-
- I really wanted to continue the trainer-serie I once started in HASH, and
- therefore I decided to to re-release this tut - now under another name :
-
- ___---__--> The Peroxide Programming Tips <--__---___
-
-
- The goal for this serie has also changed a little. Instead of just writing
- about all the common topics in graphic programming (fx. 3d-rotations, plasma,
- fire, scrolling and so on) I'll try and stay on the path I originally set when
- starting a trainer-serie with a topic as complex as texturemapped 3d-worlds :)
- I'll try and write about the stuff that other tut-writers seems to have
- overlooked - the not not-so-common effects, the things that might sound a
- little boring compared to all the colorfull demo-effects :)
- I can't promise, though, that I won't release some info on all the common stuff
- also :) So drop me a letter telling me what to do :)
-
- If you got the original HASH-tut please replace it with this version.. a FEW
- minor bugs has been corrected in this version. Also please notice the new
- E-mail address.
-
-
- *************************** ATTENTION ARTISTS!!! *****************************
-
- ARE YOU AN ARTIST ?
- DO YOU DRAW VGA-BITMAPS ?
- CAN YOU DO GFX IN ALL RESOLUTIONS - 320x200x256 AND VARIOUS SVGA-MODES ?
- DO YOU WANT TO SEE YOUR WORK IN AMAZING PRODUCTIONS ?
- CAN YOU DO GAME-GFX AS WELL AS BACKGROUNDS FOR DEMOS ?
-
- IF YOU MEET THE ABOVE TERMS (or some at least :) ), THEN DROP ME (TELEMACHOS)
- A MESSAGE OR MAIL THE GROUP AT : Peroxide@image.dk
-
- ******************************************************************************
-
-
- In this trainer I will cover the techniques I used to draw 3d-walls similar to
- those in the famous computer game DOOM. I assume the reader have a basic
- understanding of how to program the VGA-card. Also I will be using quite a bit
- of math on high school level.
- If you don't understand vector-math you can still read this doc and end up
- beeing able to code some neat gfx, but then you won't understand what you're
- doing :=)
- I will drop code in Pascal, but it should be easy to convert into other
- languages.
-
-
- If you don't know how to program the VGA-card, you should read the trainers
- written by DENTHOR of ASPHYXIA. He has written (at this date) 21 great tuturials
- covering the basic VGA coding as well as quite a few nice demo effects.
- Search the I-net for DENTHOR and you will find the trainers somewhere.
- The 10 first trainers is also available through the PCGPE 1.0
-
-
- If you want to get in contact with me, there are several ways of doing it :
-
- 1) Write me via FIDO-net : 2:235/350.22
-
- 2) E-mail me : tm@image.dk
-
- 3) Snail mail me : Kasper Fauerby
- Saloparken 226
- 8300 Odder
- Denmark
-
- 4) Call me (Voice ! ) : +45 86 54 07 60
-
-
-
- Get this serie from the major demo-related FTP-sites or from our own homepage
- (soon at least ;) ) : http://www.image.dk/~peroxide
- or directly from my own : http://www.image.dk/~tm
-
-
-
- Well... enough talk - we wanna se some code !
- ----------------------------------------------
-
- Oki... code u want - code u get!
- First of all lets make some definitions..
- My way of defining 3d-space is as follows :
-
- \ |
- \ |
- ----------------------------> X-axis
- | \
- | \
- | \
- | \
- | \
- | \
- | \> Z-axis (dissapears into the screen - in case u
- V don't understand my neat drawing :=) )
- Y-axis
-
-
-
-
- Also I have another way of doing 3d -> 2d than most others.
-
- You have probably been told to use transformations like the following :
-
- X0 := 256*x div (z-Zoff) ( + Xoffset )
- Y0 := 256*y div (z-Zoff) ( + Yoffset )
-
- Well... I say - FORGET ALL ABOUT THE CRAP ABOVE!!!!
- The ONLY way to do correct 3d-transformations is to use the following formulas:
-
- X0 := Xofs + Round(X*(Zeye/(Zeye-Z)));
- Y0 := Yofs + Round(Y*(Zeye/(Zeye-Z)));
-
- I know, I know... It takes a little more calculations to use these formulas,
- but it is worth it - believe me!
-
- With the above formulas 3 new constants has been introduced : Xofs, Yofs and
- Zeye. The three constants together makes the coordinate-set of THE EYE
- through wich the 3d-world is viewed. I personnally likes to use the following
- values : Xofs = 160, Yofs = 100 and Zeye = -200
- With these values the eye is placed in the middle of the screen (MCGA mode)
- some distance from the monitor. Yes that's correct - in your head :)
- The Xofs and Yofs defines the ORIGIN of your 3d-system - so the point (-5,4,0)
- will be at screen coord : (-5 + Xofs, 4 + Yofs), in my case (155,104)
-
- That done we can move on to the REAL subject of this trainer - the DOOM walls.
-
-
- A little speculations - boring perhaps, but nessesary :)
- ---------------------------------------------------------
-
- Well.. lets take a look at the poly we wanna texture map.
- As it's a doom-wall we know quite a few things about it.
-
- 1) Its left and right sides are ALWAYS parallel with the Y-axis of the screen.
- Well.. what does that means... It means that we can draw the mapped poly as
- a row of vertical lines - each with a different height and starting Y-pos.
- Take a look at this, and you'll know what i mean :
-
- P1
- |\
- | \
- | | \ P2
- | | |
- | | |
- | | |
- | | |
- | | / P3
- | /
- |/ ^
- P4 one of the vertical lines.
-
-
- 2) Each of the vertical lines has a CONSTANT Z-value. Now this is VERY
- important. If you follow a line with a constant Z-value, the corresponding
- line in TEXTURE-SPACE - that is, the bitmap you want to map to the poly -
- will have a CONSTANT SLOPE. Furthermore, if the line with constant z-value
- is vertical - like the ones we look at - then the line in texture-space
- will also be vertical.
- What does that mean ? Well.. it means that each vertical line in the poly
- just needs to be mapped with a scaled line from the texture.
- Now we have a formula for mapping a vertical line of the poly...
-
- 1) Find the height of the line in the poly - lets call it LH
- 2) We know the height of the texture - lets say it's 128.
- 3) Now we calculate the STEP we need to increse the Ypos in texture-space
- with for each pixel in the poly-line. STEP = 128 / LH.
- 4) we'll define the following variables :
- YposScreen, XposScreen - the start coordinate of the line in the poly.
- YposTexture, XposTexture - The position in texture-space.
- 5) Now lets map the damn thing !
-
- for i := 1 to LH do
- begin
- mem[$a000:YposScreen*320 + XposScreen] := texture[XposTexture,
- YposTexture];
- inc(YposScreen);
- YposTexture := YposTexture + STEP;
- end;
-
- Voila - here you go.. a texture mapped vertical line.
-
-
-
- Hey - neat, I hear you cry. Now this is easy.. I'll just calculate a STEP for
- movement along the X-side of the texture also.... and then just map each
- vertical line in the poly with a scaled line from the texture with the
- corresponding X-position in texture-space!
-
- Well.. try it! You will soon find out that result of this method is not to
- good! The poly IS mapped and the vertical lines looks beautiful... The problem
- is that the poly does'nt look 3d after all! It just looks like the texture has
- been squeezed a little on one side and then slammed to the screen. It surely
- does look more like a scaling than a texture mapping.
-
-
- But what's wrong then ???
- --------------------------
-
- WHY, I hear you cry! Now I thought it was so easy, and all I end up with is
- crap!
- Well.. dry your eyes - we'll find a way.
-
- The problem with the above mentioned method is that the human eye sees things
- in another way.
-
- When you look at something - a little from the side - notice that if you
- projected the object you look at to a 2d-screen, then the middle of the
- "texture" pasted to it WOULD NOT appear at the middle of the polygon !!!!
- Come on... Take a piece of paper and draw a line exactly down the middle of
- it. Then hold the paper at an arms length and look at it from the side. Does
- the line appear exactly at the middle of the projection you see of the paper ?
- NO... depending on the angle you are viewing the paper at, you will see the
- line move from the middle of the paper towards you.
-
- So what we need to do is to find a way of determing the correct X-INDEX in
- texture-space for each screen-column occupied by the poly.
- If you don't understand this, read the above paragraph again until you do!
- This is the very key to texture-mapping.
-
- Well.. we'll have to look at the wall a little more in 3d than we did before.
- Until now we have only looked at the PROJECTION of the wall.. now is the time
- to think 3d :=)
- And then again - we really don't have to! We just have to twist the view a
- little. Until now we have looked at the X,Y plane... Now lets take a look at
- the X,Z plane - ie. we look at the 3d-world from ABOVE the monitor :
-
-
- Z-axis
- ^
- |
- | P1 a ray
- | \ /
- | \ /
- | B\
- | / \
- |(0,0,0)/ P2 > A wall seen from above.
- |/ /
- ----SL---------------O-----A----------SR----> X-axis
- ^ (-160,0,0) | / (50,0,0) ^ (160,0,0)
- | /
- | /
- | /
- E
- ^
- eye (0,0,-200)
-
-
- Well.. neat ehh? If we look at the walls from above we can just look at them
- as LINES in a 2d space!
- Now, the point SL defines the left side of our computer monitor and SR
- defines the right side of our monitor.
- If we then makes one point along the X-axis equal 1 pixel along the screen
- then all we need to do when we want to know the x-index in the wall
- corresponding to a screen-column, is to calculate the intersection between
- the line made by the wall and the line drawn from the eye, through the screen-
- column and finaly through the wall-line. This is called ray-casting.
-
- As an example look at the ray drawn above. We know that the point A lies on
- the poly we get from projecting the wall P1,P2 to the screen. To find the
- correct index in texture-space we draw a virtual line from the eye E, through
- the point A and then finds the intersection with the wall - namely the point B.
- If we look at the drawing it looks like B is about 60% down the wall... If the
- texture is 128 pixels wide, then we know the correct X-index would be :
- 128 * 60% = 76.8 ... Rounded to 77. Ie. the vertical line at point A should
- be mapped with line 77 in the texture - even though we can clearly see, that
- A would be one of the first lines in the poly !
-
- Here you go... do this with all of the screen columns occupied by the poly,
- and you have done a perspectively correct texture-maping!
-
-
- Now for the really ugly part... the vector math :=)
- ----------------------------------------------------
-
- But in real life we can't just say : Humm... it looks like the intersection
- between the wall and the ray through screen coloumn X is about xx % down the
- wall..
- We need to find a way of CALCULATING the exact intersection.
- Now it's time to introduce our good friend : -=[ Mr. Vector ]=-.
-
-
- If we set up the parametric equation for the ray and a parametric equation for
- the line representing the wall, all we have to do is to calculate the
- intersection between those two equations.
-
- Now, as long as we don't move our eye position during runtime we can
- precalculate all the parametric equations we'll ever need for our rays.
- All we have to do is to store the X and Z vector components in a small array.
- Since all the rays has an angle associated with it - namely the angle
- between the ray and the Z-axis - we can calculate a ray's vector components as
- COS and SIN of this angle.
-
- Here is how I did it :
-
- PROCEDURE Build_table;
- VAR
- counter : integer;
- angle : real;
- BEGIN
- for counter:=0 to 319 do
- BEGIN
- angle:=ARCTAN((counter-Xofs)/ABS(Zeye)*((2*pi)/360));
- raytable[counter,1]:=Cos((2*pi)/360)*angle);
- raytable[counter,2]:=Sin((2*pi)/360)*angle);
- END;
- END;
-
- Now the array RAYTABLE contains the vector components of the ray through each
- screen column. I stored the components so Raytable[X,1] was the Z-comp and
- Raytable[X,2] was the Xcomp.
-
-
- So the parametric equation for a ray is :
-
- RayX = s * (X component of the ray)
- RayZ = s * (Z component of the ray) + Zeye
-
-
- Now all we need is the equation for the wall... Let the wall be difined by the
- points P1 and P2 - where P1 ALWAYS is the point farthest to the left.
- Then let P1X be the X-value for P1 and let P1Z be the Z-value. Guess what we'll
- call the X and Z value for P2 :=)
-
- To make the wall a vector subtract the left end values from the right end
- values.
- Ie. the parametric equation for the wall is :
-
- WallX = t * (P2X - P1X) + P1X
- WallZ = t * (P2Y - P1Y) + P1Y
-
-
- Now where does these two lines cross? They cross where WallX = RayX and
- WallZ = RayZ. So we get :
-
- s * (X component of the ray) = t * (P2X - P1X) + P1X
- s * (Z component of the ray) + Zeye = t * (P2Y - P1Y) + P1Y
-
- Now, because we used the hole wall to define the vector used in the line
- equation we know that if we solve the two equations above for t, then t will
- range from 0 to 1. It'll be the percent of the wall length where the two lines
- collide! To get the correct X-position in the texture multiply t by the
- textures width - in this case 128.
-
- Lets make our brains bleed, shall we ?
- We don't care about the variable s so lets isolate it in both equations :
-
- t *(P2X-P1X) + P1X
- 1) s = --------------------
- Xcomp
-
- t*(P2Z-P1Z) + P1Z - Zeye
- 2) s = --------------------------
- Zcomp
-
-
- well ... combine the two and get :
-
- t * (P2X-P1X) + P1X t * (P2Z-P1Z) + P1Z - Zeye
- -------------------- = ----------------------------
- Xcomp Zcomp
-
-
- We don't like that divisor - lets get rid of it :
-
- t*Zcomp*(P2X-P1X) + P1X*Zcomp = t*Xcomp*(P2Z-P1Z) + P1Z*Xcomp - Zeye*Xcomp
-
- well ... lets get the t's on one side :
-
- t*(Zcomp*(P2X-P1X) - Xcomp*(P2Z-P1Z)) + P1X*Zcomp = P1Z*Xcomp - Zeye*Xcomp
-
- humm... lets isolate t :
-
- P1Z*Xcomp - Zeye*Xcomp - P1X*Zcomp
- t = -----------------------------------
- Zcomp*(P2X-P1X) - Xcomp*(P2Z-P1Z)
-
- finally lets save a costly multiply :
-
-
- Xcomp * (P1Z-Zeye) - P1X*Zcomp
- t = ------------------------------------
- Zcomp*(P2X-P1X) - Xcomp*(P2Z-P1Z)
-
-
- TADA... easy huh ? Now you got all you need to do texture mapping with correct
- perspective :)
-
-
-
- What ? You're to lazy to do the routines yourself ?
- ----------------------------------------------------
-
- Well.. this section should be superfluous, but I'll do it anyway :)
- In this section I'll drop some pascal code to do texture mapping.
-
- I'll write it in plain pascal though, so you'll have to do the optimizations
- yourself - that is : use fixed point math, add clipping, convert it to
- assembler and find a better way of storing the texture data.
- As for the last point watch out for my next trainer, wich will cover the
- use of memory above the 640K base memory.
-
- Also you c(sh)ould add features as "see through" colors, to make your engine
- capeable of doing textures with windows, grates and more.
- All this is fortunately quite easy to do - trust me 8)
-
- Anyway here you go :
-
-
- VAR raytable : Array[0..319,1..2] of real; {each ray is Z-comp, X-comp}
- walltex : Array[0..128,0..128] of byte;
-
-
-
- FUNCTION index(X1,Z1,X2,Z2,C : integer) : real;
- BEGIN
- index:=(raytable[C,2]*(Z1-Zeye)-X1*raytable[C,1])/((X2-X1)*
- raytable[C,1]-(Z2-Z1)*raytable[C,2]);
- END; {given the line X1,Z1,X2,Z2 this function returns the parameter}
- {for the intersection of the line and the ray through coloumb C}
-
-
- PROCEDURE Loadtexture(Picture : string);
- VAR
- i,j : integer;
- BEGIN
- Display(picture,Vaddr,0,0);
- For i:=0 to 128 DO
- For j:=0 to 128 DO
- WallTex[j,i]:=GetPixel(j,i,Vaddr);
- Clear(0,Vaddr);
- END;
-
-
- PROCEDURE Texturemap(X1,Y1,X2,Y2,X3,Y3,X4,Y4,X1wall,Z1wall,X2wall,Z2wall
- :integer);
- VAR
- Xpos,Ypos,height : real;
- topslope,botslope : real;
- step : real;
- offset: word;
- width : integer;
- i,j : integer;
-
- BEGIN
- topslope:=(Y2-Y1)/(X2-X1);
- botslope:=(Y3-Y4)/(X3-X4);
- width:=ABS(X2-X1);
- height:=Y4-Y1+topslope-botslope;
-
- For i:=0 to width DO
- BEGIN
- Ypos:=0;
- Height:=height-topslope+botslope;
- step:=128/height;
- offset:=X1+i+(Round(topslope*i+Y1)*320);
-
- Xpos:=index(X1wall,Z1wall,X2wall,Z2wall,X1+i)*128;
- If Xpos<0 THEN Xpos:=0; {in case of small rounding-errors}
- If Xpos>128 THEN Xpos:=128; {we set Xpos to outer rim of texture}
- For j:=0 to Round(height) DO
- BEGIN
- Mem[$a000:offset]:=WallTex[Round(Xpos),Round(Ypos)];
- Inc(offset,320);
- Ypos:=Ypos+step;
- END;
- END;
- END;
-
-
- Well... off with you ! Go make some stunning games and amaze the world :=)
-
-
-
- Last remarks....
- -----------------
-
- Well, that's about all for now.
- Hope you found this doc useful - and BTW : If you DO make anything public using
- these techniques please mention me in your greets or where ever you se fit.
- I DO love to see my name in a greeting :=)
-
- For a simplified demo of my current 3d-engine check out the archive demo.zip
- It contains a demo of my wall-drawing routines, but I have left out my monster
- routines 'cause they are still under development and the AI is not too great
- at this point either :)
- Humm.. the GFX is from the computer game MIGHT & MAGIC 4 and 5. I hope New
- World Computing won't sue me for using their great gfx in this tut :=)
- Even though my engine is running in stepmode these routines should be fast
- enough to make a fluid movement engine like DOOM.
- I use stepmode 'cause I like that kind of engines and I find them better when
- you're writing RPG's and not simple action games.
-
-
- As mentioned earlier, I'll do another trainer in near future about memory
- management. But if you wish me to continue doing trainers, you'll have to let
- me know someone's reading them. Also I would like to know if you find any
- errors in this text. No - NOT spelling errors! I live in Denmark, and in DK we
- speak DANISH! You're lucky to get this doc in english!
- Anyway - respond to this tut, or the serie will probably die!
-
-
- Keep on coding and CuL8'er M8's
-
- Telemachos - April '97
-